package com.example.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.example.common.Constants;
import com.example.common.enums.ResultCodeEnum;
import com.example.common.enums.RoleEnum;
import com.example.entity.Account;
import com.example.entity.DiskFiles;
import com.example.entity.Share;
import com.example.entity.Trash;
import com.example.exception.CustomException;
import com.example.mapper.DiskFilesMapper;
import com.example.utils.TokenUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 网盘文件信息表业务处理
 **/
@Service
public class DiskFilesService {
    // 文件上传存储路径  本地磁盘地址
    private static final String filePath = System.getProperty("user.dir") + "/disk/";

    // 设置打印错误信息的log
    private static final Logger log = LoggerFactory.getLogger(DiskFilesService.class);

    @Value("${server.port:9090}")
    private String port;

    @Value("${ip:localhost}")
    private String ip;

    @Resource
    private DiskFilesMapper diskFilesMapper;
    @Resource
    private TrashService trashService;

    @Resource
    private ShareService shareService;

    /**
     * 新增(文件上传，新建文件)
     */
    public void add(MultipartFile file, String name, String folder, Integer folderId) {
        // 文件路径  文件夹名称  是否是文件夹  文件id

        // 创建一个DiskFiles对象
        DiskFiles diskFiles = new DiskFiles();
        String now = DateUtil.now();
        // 设置创建时间
        diskFiles.setCrateTime(now);
        // 设置修改时间
        diskFiles.setUpdateTime(now);
        // 是否是文件
        diskFiles.setFolder(folder);
        // 文件夹名称
        diskFiles.setName(name);
        // 默认是文件夹  后面如果是文件的话 会覆盖这个值
        diskFiles.setType(Constants.FILE_TYPE_FOLDER);
        Account currentUser = TokenUtils.getCurrentUser();
        // 只有用户才可以创建文件夹
        if (RoleEnum.USER.name().equals(currentUser.getRole())) {
            // 设置用户id
            diskFiles.setUserId(currentUser.getId());
        }
        // 设置文件/文件夹id
        diskFiles.setFolderId(folderId);

        // 上半部分不管是创建文件夹还是上传文件都需要传入的参数

        // 上传文件操作
        if (Constants.NOT_FOLDER.equals(folder)) {
            // 获取文件名称
            String originalFilename = file.getOriginalFilename();
            // 设置文件名称
            diskFiles.setName(originalFilename);
            // 获取文件后缀
            String extName = FileUtil.extName(originalFilename);
            // 设置文件后缀
            diskFiles.setType(extName);

            // 创建一个时间戳
            long flag = System.currentTimeMillis();
            // 创建新的文件名称
            String fileName = flag + "-" + originalFilename;
            // 判断存储路径是否存在
            if (!FileUtil.exist(filePath)) {
                // 不存在就进行创建
                FileUtil.mkdir(filePath);
            }
            // 计算文件的大小
            try {
                byte[] bytes = file.getBytes();
                // 精确计算，保留三位小数，进行四舍五入
                double size = BigDecimal.valueOf(bytes.length).divide(BigDecimal.valueOf(1024), 3, RoundingMode.HALF_UP).doubleValue();
                // 设置文件大小
                diskFiles.setSize(size);

                // 文件上传
                file.transferTo(new File(filePath + fileName));
            } catch (Exception e) {
                log.error("网盘文件上传错误", e);
            }
            // 文件下载路径
            String url = "http://" + ip + ":" + port + "/diskFiles/download/" + fileName;
            // 设置文件下载路径
            diskFiles.setFile(url);
        }
        // 最后插入数据
        diskFilesMapper.insert(diskFiles);


        // 文件夹才会有rootFolderId
        // 如果是文件
        // 1外层有文件夹才会有rootFolderId
        // 2如果没有文件夹就不会有rootFolderId

        if (folderId != null) {   //  folderId不为空表示外层又一层文件夹，
            DiskFiles parentFolder = this.selectById(folderId);  // 获取到外层的目录
            Integer rootFolderId = parentFolder.getRootFolderId();
            diskFiles.setRootFolderId(rootFolderId); // 设置最外层文件夹的id
        } else {
            // 这里当前文件夹的时候才会设置rootFolderId  这里表示创建新的文件夹
            if (Constants.IS_FOLDER.equals(folder)) {  // 当前是新建目录操作
                Integer diskFilesId = diskFiles.getId();  // 刚才插入到数据库的文件的ID
                diskFiles.setRootFolderId(diskFilesId);// 设置最外层文件夹的id
            }
            // 如果是文件的不用设置rootFolderId
        }
        this.updateById(diskFiles);  // 更新 root_folder_id 字段的值
    }

    /**
     * 网盘文件下载
     */
    public void download(String fileName, HttpServletResponse response) {
        OutputStream os;
        try {
            if (StrUtil.isNotEmpty(fileName)) {
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(filePath + fileName);
                System.out.println("------------------------");
                System.out.println("下载文件名称：" + fileName);
                System.out.println("文件下载存放地址" + filePath + fileName);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            System.out.println("文件下载失败");
        }
    }


    /**
     * 网盘文件预览
     *
     * @param id
     * @param response
     */
    public void preview(Integer id, HttpServletResponse response) {
        DiskFiles diskFiles = this.selectById(id);
        if (diskFiles == null) {
            throw new CustomException(ResultCodeEnum.FILE_NOT_FOUND);
        }
        // 定义预览类型格式
        ArrayList<String> typeList = CollUtil.newArrayList("jpg", "jpeg", "png", "pdf", "gif");
        // 获取的文件类型格式不存在数组里则抛出异常
        if (!typeList.contains(diskFiles.getType())) {
            throw new CustomException(ResultCodeEnum.TYPE_NOT_SUPPORT);
        }
        OutputStream os;
        try {
            // 截取文件
            String file = diskFiles.getFile();
            // 从尾部的/开始进行截取
            String endFile = file.substring(file.lastIndexOf("/"));
            response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(diskFiles.getName(), "UTF-8"));
            byte[] bytes = FileUtil.readBytes(filePath + endFile);
            os = response.getOutputStream();
            os.write(bytes);
            os.flush();
            os.close();

        } catch (Exception e) {
            System.out.println("文件预览失败");
        }
    }


    /**
     * 移入垃圾箱
     *
     * @param id
     */
    @Transactional
    public void trashById(Integer id) {
        // 查询当前文件
        DiskFiles diskFiles = this.selectById(id);
        // 递归删除子节点
        this.deepTrash(id);

        // 移入垃圾箱
        Trash trash = new Trash();
        trash.setTime(DateUtil.now());
        trash.setUserId(diskFiles.getUserId());
        trash.setFileId(diskFiles.getId());
        trash.setSize(diskFiles.getSize());
        trash.setName(diskFiles.getName());

        trashService.add(trash);
    }

    /**
     * 递归删除
     */
    private void deepTrash(Integer id) {
        DiskFiles diskFiles = this.selectById(id);
        if (diskFiles == null) {
            return;
        }

        diskFilesMapper.trashById(id); // 删除当前文件

        // 判断是否是文件
        if (Constants.NOT_FOLDER.equals(diskFiles.getFolder())) {  // 是文件
            return;
        }

        // 当前的文件夹id就是下一层文件的FolderId
        List<DiskFiles> list = diskFilesMapper.selectByFolderId(id);

        // 判断是否为空
        if (list == null || list.size() == 0) {
            return;
        }
        for (int i = 0; i < list.size(); i++) {
            // 进行递归寻找子节点
            this.deepTrash(list.get(i).getId());
        }
    }

    /**
     * 批量移入垃圾箱
     *
     * @param ids
     */
    public void trashBatch(List<Integer> ids) {
        for (Integer id : ids) {
            this.trashById(id);
        }
    }


    /**
     * 删除
     */
    public void deleteById(Integer id) {
        this.deepDelete(id);
        // 删除回收站的文件记录
        trashService.deleteByFileId(id);
    }

    /**
     * 递归彻底删除
     */
    private void deepDelete(Integer id) {
        DiskFiles diskFiles = this.selectById(id);
        if (diskFiles == null) {
            return;
        }
        diskFilesMapper.deleteById(id);  // 删除当前的文件记录

        if (Constants.NOT_FOLDER.equals(diskFiles.getFolder())) {  // 是文件
            // 删除文件
            String file = diskFiles.getFile();
            // 地址
            String path = filePath + file.substring(file.lastIndexOf("/"));
            // 删除地址路劲
            FileUtil.del(path);

        }
        // 查询到所有的文件
        List<DiskFiles> children = diskFilesMapper.selectByFolderId(id);
        // 进行非空判断
        if (CollUtil.isEmpty(children)) {
            return;
        }
        // 遍历集合
        for (DiskFiles child : children) {
            // 递归寻找子节点
            this.deepTrash(child.getId());
        }

    }

    /**
     * 批量删除
     */
    public void deleteBatch(List<Integer> ids) {
        for (Integer id : ids) {
            this.deleteById(id);
        }
    }

    /**
     * 修改
     */
    public void updateById(DiskFiles diskFiles) {
        diskFilesMapper.updateById(diskFiles);
    }

    /**
     * 根据ID查询
     */
    public DiskFiles selectById(Integer id) {
        return diskFilesMapper.selectById(id);
    }

    /**
     * 查询所有
     */
    public List<DiskFiles> selectAll(DiskFiles diskFiles) {
        Account currentUser = TokenUtils.getCurrentUser();
        // 根据用户id查询
        if (RoleEnum.USER.name().equals(currentUser.getRole())) {
            diskFiles.setUserId(currentUser.getId());
        }
        return diskFilesMapper.selectAll(diskFiles);
    }

    /**
     * 分页查询
     */
    public PageInfo<DiskFiles> selectPage(DiskFiles diskFiles, Integer pageNum, Integer pageSize) {
        // 进行分页
        PageHelper.startPage(pageNum, pageSize);
        // 查询数据库，返回的是集合
        List<DiskFiles> list = diskFilesMapper.selectAllData(diskFiles);
        // 返回
        return PageInfo.of(list);
    }

    /**
     * 查询父级文件夹的名称
     *
     * @param id
     * @param list
     * @return
     */

    public List<DiskFiles> selectFolderName(Integer id, List<DiskFiles> list) {
        // 查询到当前的文件
        DiskFiles diskFiles = this.selectById(id);
        if (diskFiles == null) return list;
        // 获取当前文件夹的名称,存入list集合
        list.add(diskFiles);
        // 获取父级id
        Integer parentFolderId = diskFiles.getFolderId();
        System.out.println("查询的父级文件夹id：" + parentFolderId);
        // 当父级id为空的时候就表示外层没有文件夹了
        if (parentFolderId == null) {
            return list;
        }
        // 这里进行递归操作
        return this.selectFolderName(parentFolderId, list);
    }

    public List<Trash> selectTrash() {
        Integer userId = null;
        Account currentUser = TokenUtils.getCurrentUser();
        if (RoleEnum.USER.name().equals(currentUser.getRole())) {
            userId = currentUser.getId();
        }
        List<Trash> list = diskFilesMapper.selectTrash(userId);
        return list;
    }

    /**
     * 还原
     */
    public void restore(Integer id) {
        System.out.println("这个id是：" + id);
        // 还原，就是把delete的状态改回1
        this.deepRestore(id);
        // 然后删除回收站里面的数据
        trashService.deleteByFileId(id);
    }

    /**
     * 递归还原
     */
    public void deepRestore(Integer id) {
        DiskFiles diskFiles = this.selectById(id);
        if (diskFiles == null) {
            return;
        }
        diskFilesMapper.restoreById(id);

        // 判断是否是文件
        if (Constants.NOT_FOLDER.equals(diskFiles.getFolder())) {  // 是文件
            return;
        }

        // 当前的文件夹id就是下一层文件的FolderId
        List<DiskFiles> list = diskFilesMapper.selectAllByFolderId(id);

        // 判断是否为空
        if (list == null || list.size() == 0) {
            return;
        }
        for (int i = 0; i < list.size(); i++) {
            // 进行递归寻找子节点
            this.deepRestore(list.get(i).getId());
        }

    }

    /**
     * 拷贝的原理是：根据id拷贝最外层的文件\文件夹，再根据递归，将原来的数据拷贝到新的文件夹。
     * 注意：拷贝的时候要重新设置folderId。
     *
     * @param id
     * @param folderId 最外层的是否没有folderId，递归的时候，上一层的id，就是子类下的folderId
     */
    // 进行递归拷贝
    public void copy(Integer id, Integer folderId) {
        // 查询
        DiskFiles diskFiles = this.selectById(id);

        if (diskFiles == null) {
            return;
        }

        String now = DateUtil.now();
        diskFiles.setId(null);
        diskFiles.setUpdateTime(now);
        diskFiles.setCrateTime(now);
        // 递归之前需要先设置folderId
        diskFiles.setFolderId(folderId);
        diskFiles.setName(diskFiles.getName() + "-拷贝");
        // 插入数据
        diskFilesMapper.insert(diskFiles);
        // 是目录（文件夹的操作）
        if (Constants.IS_FOLDER.equals(diskFiles.getFolder())) {
            // 递归
            List<DiskFiles> list = diskFilesMapper.selectByFolderId(id); // 排除被删除的文件或者文件夹

            // 递归出口
            if (list == null) {
                return;
            }

            // 递归
            for (int i = 0; i < list.size(); i++) {
                // 传入当前的id，和folderId
                this.copy(list.get(i).getId(), diskFiles.getId());
            }
        }

    }

    public Share share(DiskFiles diskFiles) {
        Share share = new Share();
        share.setName(diskFiles.getName());
        share.setFileId(diskFiles.getId());
        // 获取当前的时间
        share.setShareTime(DateUtil.now());
        // 根据token获取用户
        Account user = TokenUtils.getCurrentUser();
        if (RoleEnum.USER.name().equals(user.getRole())) {
            share.setUserId(user.getId());
        }

        // 得到传递过来的天数
        Integer days = diskFiles.getDays();
        DateTime dateTime = DateUtil.offsetDay(new Date(), days);
        System.out.println(DateUtil.formatTime(dateTime));
        // 结束时间
        share.setEndTime(DateUtil.formatTime(dateTime));
        // 生成一个唯一的标识  作为本次分享的验证码
        share.setCode(IdUtil.getSnowflakeNextIdStr());

        share.setType(diskFiles.getType());
        shareService.add(share);
        return share;
    }
}